import json
import sys
import time
from django.core.management.base import BaseCommand
from spider.models import SubDetailItem
from process.core.process import run_iter


class Command(BaseCommand):
    help = "运行处理管线"

    def add_arguments(self, parser):
        parser.add_argument('--task-id', type=int)
        parser.add_argument('--limit', type=int, default=20, help='每轮处理条数，0 表示处理全部')
        parser.add_argument('--extra', type=str, default='')
        parser.add_argument('--workers', type=int, default=5)
        parser.add_argument('--interval', type=int, default=30, help='循环间隔秒，>0 则进入循环模式')
        parser.add_argument('--provider', type=str, default=None, help='大模型提供者：qwen-local|qwen-cloud|lmstudio|ollama（不填用系统默认）')
        parser.add_argument('--model', type=str, default=None, help='调用模型名称，如 Qwen3-Instruct')

    def handle(self, *args, **options):
        start_ts = time.perf_counter()
        extra = {}
        if options.get('extra'):
            try:
                extra = json.loads(options['extra'])
            except Exception:
                extra = {}
        interval = options.get('interval') or 0
        tid = options.get('task_id')
        bar_len = 30
        label_map = {
            'winning_result_announcement': '中标结果公示',
            'tender_announcement': '招标公告',
            'bid_opening_record': '开标记录',
            'bid_opening_detail': '开标详情',
            'bid_evaluation_result': '评标结果',
            'bid_candidate': '中标候选人',
            'transaction_result': '合同签约',
            'tender_clarification': '澄清文件',
            'other': '其他',
        }
        if interval and interval > 0:
            try:
                cum_total = 0
                cum_ok = 0
                cum_failed = 0
                while True:
                    loop_start = time.perf_counter()
                    qs = SubDetailItem.objects.filter(is_processed=False)
                    if tid is not None and tid > 0:
                        qs = qs.filter(task_id=tid)
                    if options.get('limit') and options.get('limit') > 0:
                        expected = options.get('limit')
                    else:
                        expected = qs.count()
                    if expected == 0:
                        bar = '-' * bar_len
                        line = f"\r[{bar}] 0/0 成功 0 失败 0 | 累计 成功 {cum_ok} 失败 {cum_failed} 总计 {cum_total} | 空闲 {interval}s"
                        sys.stdout.write(line)
                        sys.stdout.flush()
                        sys.stdout.write('\n')
                        time.sleep(interval)
                        continue
                    processed = 0
                    ok = 0
                    failed = 0
                    stream = run_iter(
                        task_id=(tid if (tid is not None and tid > 0) else None),
                        limit=options.get('limit'),
                        random_pick=False,
                        workers=options.get('workers') or 1,
                        model=options.get('model'),
                        provider=options.get('provider'),
                        **extra
                    )
                    for r in stream:
                        processed += 1
                        s = r.get('summary') or {}
                        url = s.get('url') or r.get('url')
                        project_name = s.get('project_name')
                        amount = s.get('amount')
                        t = r.get('type') or 'other'
                        t_cn = label_map.get(t, '其他')
                        conf = (r.get('classification') or {}).get('confidence')
                        if r.get('ok') and r.get('record_id'):
                            try:
                                if t == 'tender_announcement':
                                    from deal.models import TenderAnnouncement
                                    ta = TenderAnnouncement.objects.filter(id=r.get('record_id')).first()
                                    if ta:
                                        project_name = project_name or ta.project_name
                                        amount = amount or (str(ta.budget_amount) if ta.budget_amount is not None else None)
                                elif t == 'winning_result_announcement':
                                    from deal.models import WinningResultAnnouncement
                                    wa = WinningResultAnnouncement.objects.filter(id=r.get('record_id')).first()
                                    if wa:
                                        project_name = project_name or (wa.project.project_name if wa.project else None)
                                        amount = amount or (str(wa.winning_amount) if wa.winning_amount is not None else None)
                                elif t == 'transaction_result':
                                    from deal.models import TransactionResult
                                    tr = TransactionResult.objects.filter(id=r.get('record_id')).first()
                                    if tr:
                                        project_name = project_name or (tr.project.project_name if tr.project else None)
                                        amount = amount or (str(tr.contract_amount) if tr.contract_amount is not None else None)
                            except Exception:
                                pass
                        if r.get('ok'):
                            ok += 1
                        else:
                            failed += 1
                        if not project_name:
                            reason = ((r.get('classification') or {}).get('payload') or {}).get('reason') or ''
                            import re
                            m_p = re.search(r"项目名称[(:：]([^)，）]+)", reason)
                            if m_p:
                                project_name = m_p.group(1).strip()
                        if not amount:
                            reason = ((r.get('classification') or {}).get('payload') or {}).get('reason') or ''
                            import re
                            m_a = re.search(r"(\d+(?:\.\d+)?)\s*万元", reason)
                            if m_a:
                                amount = m_a.group(1) + "万元"
                        obj = {
                            "task_id": r.get("task_id"),
                            "stored": bool(r.get("ok")),
                            "url": url,
                            "project_name": project_name,
                            "amount": amount,
                            "type": t,
                            "class_cn": t_cn,
                            "confidence": conf,
                        }
                        self.stdout.write(json.dumps(obj, ensure_ascii=False, indent=2))
                        pct = 0 if expected == 0 else int(processed * 100 / max(expected, 1))
                        filled = int(bar_len * pct / 100)
                        bar = '#' * filled + '-' * (bar_len - filled)
                        line = f"\r[{bar}] {processed}/{expected} 成功 {ok} 失败 {failed} | 累计 成功 {cum_ok+ok} 失败 {cum_failed+failed} 总计 {cum_total+processed}"
                        sys.stdout.write(line)
                        sys.stdout.flush()
                    sys.stdout.write('\n')
                    cum_total += processed
                    cum_ok += ok
                    cum_failed += failed
                    loop_elapsed = time.perf_counter() - loop_start
                    loop_avg = (loop_elapsed / max(processed, 1)) if processed > 0 else 0.0
                    self.stdout.write(f"本轮完成：total={processed} success={ok} failed={failed} | 耗时:{loop_elapsed:.2f}s 平均:{loop_avg:.2f}s/条")
                    time.sleep(interval)
            except KeyboardInterrupt:
                self.stdout.write(self.style.WARNING('已停止循环处理'))
                return
        total = 0
        success = 0
        failed = 0
        qs = SubDetailItem.objects.filter(is_processed=False)
        if tid is not None and tid > 0:
            qs = qs.filter(task_id=tid)
        if options.get('limit') and options.get('limit') > 0:
            expected = options.get('limit')
        else:
            expected = qs.count()
        stream = run_iter(
            task_id=(tid if (tid is not None and tid > 0) else None),
            limit=options.get('limit'),
            random_pick=False,
            workers=options.get('workers') or 1,
            model=options.get('model'),
            provider=options.get('provider'),
            **extra
        )
        for r in stream:
            total += 1
            s = r.get('summary') or {}
            url = s.get('url') or r.get('url')
            project_name = s.get('project_name')
            amount = s.get('amount')
            t = r.get('type') or 'other'
            t_cn = label_map.get(t, '其他')
            conf = (r.get('classification') or {}).get('confidence')
            if r.get('ok') and r.get('record_id'):
                try:
                    if t == 'tender_announcement':
                        from deal.models import TenderAnnouncement
                        ta = TenderAnnouncement.objects.filter(id=r.get('record_id')).first()
                        if ta:
                            project_name = project_name or ta.project_name
                            amount = amount or (str(ta.budget_amount) if ta.budget_amount is not None else None)
                    elif t == 'winning_result_announcement':
                        from deal.models import WinningResultAnnouncement
                        wa = WinningResultAnnouncement.objects.filter(id=r.get('record_id')).first()
                        if wa:
                            project_name = project_name or (wa.project.project_name if wa.project else None)
                            amount = amount or (str(wa.winning_amount) if wa.winning_amount is not None else None)
                    elif t == 'transaction_result':
                        from deal.models import TransactionResult
                        tr = TransactionResult.objects.filter(id=r.get('record_id')).first()
                        if tr:
                            project_name = project_name or (tr.project.project_name if tr.project else None)
                            amount = amount or (str(tr.contract_amount) if tr.contract_amount is not None else None)
                except Exception:
                    pass
            if r.get('ok'):
                success += 1
            else:
                failed += 1
            if not project_name:
                reason = ((r.get('classification') or {}).get('payload') or {}).get('reason') or ''
                import re
                m_p = re.search(r"项目名称[(:：]([^)，）]+)", reason)
                if m_p:
                    project_name = m_p.group(1).strip()
            if not amount:
                reason = ((r.get('classification') or {}).get('payload') or {}).get('reason') or ''
                import re
                m_a = re.search(r"(\d+(?:\.\d+)?)\s*万元", reason)
                if m_a:
                    amount = m_a.group(1) + "万元"
            obj = {
                "task_id": r.get("task_id"),
                "stored": bool(r.get("ok")),
                "url": url,
                "project_name": project_name,
                "amount": amount,
                "type": t,
                "class_cn": t_cn,
                "confidence": conf,
            }
            self.stdout.write(json.dumps(obj, ensure_ascii=False, indent=2))
            pct = 0 if expected == 0 else int(total * 100 / max(expected, 1))
            filled = int(bar_len * pct / 100)
            bar = '#' * filled + '-' * (bar_len - filled)
            line = f"\r[{bar}] {total}/{expected} 成功 {success} 失败 {failed}"
            sys.stdout.write(line)
            sys.stdout.flush()
        sys.stdout.write('\n')
        final_bar = '[' + '#' * bar_len + ']'
        self.stdout.write(f"进度: {final_bar} {total}/{expected} 成功:{success} 失败:{failed}")
        elapsed = time.perf_counter() - start_ts
        avg = (elapsed / max(total, 1)) if total > 0 else 0.0
        self.stdout.write(f"耗时: {elapsed:.2f}s 平均: {avg:.2f}s/条")
